package drds.plus.sql_process.optimizer.pre_processor;

import drds.plus.common.properties.ConnectionProperties;
import drds.plus.sql_process.abstract_syntax_tree.ObjectCreateFactory;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.Item;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.column.Column;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.*;
import drds.plus.sql_process.abstract_syntax_tree.node.Node;
import drds.plus.sql_process.abstract_syntax_tree.node.query.Join;
import drds.plus.sql_process.abstract_syntax_tree.node.query.Query;
import drds.plus.sql_process.abstract_syntax_tree.node.query.TableQuery;
import drds.plus.sql_process.optimizer.chooser.EmptyResultFilterException;
import drds.plus.sql_process.type.Type;
import drds.plus.sql_process.utils.DnfFilters;
import drds.plus.sql_process.utils.Filters;
import drds.plus.sql_process.utils.OptimizerUtils;
import drds.plus.util.ExtraCmd;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * 预先处理filter条件
 *
 * <pre>
 * 1. 判断永真/永假式,短化路径
 * 如： false or a = 1，优化为 a = 1
 * 如： true or a = 1，优化为true
 * 如： false or false，优化为{@linkplain EmptyResultFilterException}异常
 *
 * 2. 调整下1 = id的列信息，优化为id = 1
 * 如：1 < a，调整为 a > 1
 * 如：1 <= a，调整为 a >= 1
 * 如：1 > a，调整为 a < 1
 * 如：1 >= a，调整为 a <= 1
 * 其他情况，仅仅是交换下位置
 *
 * 3. 根据column列，调整下value的类型
 * 如：a = 1，如果a是LONG型，会将文本1转化为Long. (sql解析后都会是纯文本信息)
 * </pre>
 */
public class FilterPreProcessor {

    /**
     * 处理逻辑见类描述 {@linkplain FilterPreProcessor}
     */
    public static Query optimize(Query query, boolean typeConvert, Map<String, Object> extraCmd) {
        query = preProcess(query, typeConvert, extraCmd);
        return query;
    }

    private static Query preProcess(Query query, boolean typeConvert, Map<String, Object> extraCmd) {
        query.setOtherJoinOnFilter(processFilter(query.getOtherJoinOnFilter(), typeConvert, extraCmd));
        query.having(processFilter(query.getHaving(), typeConvert, extraCmd));
        query.setWhereAndSetNeedBuild(processFilter(query.getWhere(), typeConvert, extraCmd));
        query.setIndexQueryKeyFilterAndSetNeedBuild(processFilter(query.getIndexQueryKeyFilter(), typeConvert, extraCmd));
        query.setResultFilterAndSetNeedBuild(processFilter(query.getResultFilter(), typeConvert, extraCmd));
        if (query instanceof TableQuery) {
            ((TableQuery) query).setIndexQueryValueFilter(processFilter(((TableQuery) query).getIndexQueryValueFilter(), typeConvert, extraCmd));
        }

        if (query instanceof Join) {
            for (int i = 0; i < ((Join) query).getJoinFilterList().size(); i++) {
                processFilter(((Join) query).getJoinFilterList().get(i), typeConvert, extraCmd);
            }
        }

        for (Node node : query.getNodeList()) {
            preProcess((Query) node, typeConvert, extraCmd);
        }

        return query;
    }

    public static Filter processFilter(Filter root, boolean typeConvert, Map<String, Object> extraCmd) {
        if (root == null) {
            return null;
        }

        root = processOneFilter(root, typeConvert, extraCmd); // 做一下转换处理
        root = processGroupFilter(root, extraCmd); // 合并一下or group
        root = shortFilter(root); // 短路一下
        root = DnfFilters.merge(root);// 合并一下flter
        return root;
    }

    /**
     * 处理只有一个子条件
     */
    private static Filter processOneFilter(Filter root, boolean typeConvert, Map<String, Object> extraCmd) {
        if (root == null) {
            return null;
        }
        if (root instanceof BooleanFilter) {
            return processBoolFilter(root, typeConvert, extraCmd);
        } else if (root instanceof OrsFilter) {
            OrsFilter orsFilter = (OrsFilter) root;
            List<Filter> filterList = new LinkedList<Filter>();
            for (Filter filter : orsFilter.getFilterList()) {
                Filter procedOneFilter = processOneFilter(filter, typeConvert, extraCmd);
                if (procedOneFilter != null) {
                    filterList.add(procedOneFilter);
                }
            }
            if (filterList.isEmpty()) {
                return null;
            }
            if (filterList.size() == 1) {
                return filterList.get(0);
            }
            orsFilter.setFilterList(filterList);
            return orsFilter;
        } else if (root instanceof LogicalOperationFilter) {
            LogicalOperationFilter logicalOperationFilter = (LogicalOperationFilter) root;
            List<Filter> filterList = new LinkedList<Filter>();
            for (Filter subFilter : logicalOperationFilter.getFilterList()) {
                Filter filter = processOneFilter(subFilter, typeConvert, extraCmd);
                if (filter != null) {
                    filterList.add(filter);
                }
            }
            if (filterList.isEmpty()) {
                return null;
            }
            if (filterList.size() == 1) {
                return filterList.get(0);
            }
            logicalOperationFilter.setFilterList(filterList);
            return logicalOperationFilter;
        }

        return root;
    }

    /**
     * 将一个bool树中的节点合并为group，比如((A+B)+C)D转化为ED, E=(A+B+C)
     */
    private static Filter processGroupFilter(Filter filter, Map<String, Object> extraCmd) {
        if (filter == null) {
            return null;
        }
        if (isNeedExpandOrWithOther(extraCmd)) {// in 展开为or
            // 如果确认需要展开or，忽略group处理
            return filter;
        }
        if (filter.getOperation().equals(Operation.and)) {
            LogicalOperationFilter logicalOperationFilter = (LogicalOperationFilter) filter;
            for (int i = 0; i < logicalOperationFilter.getFilterList().size(); i++) {
                logicalOperationFilter.getFilterList().set(i, processGroupFilter(logicalOperationFilter.getFilterList().get(i), extraCmd));
            }
        } else if (filter.getOperation().equals(Operation.or)) {
            LogicalOperationFilter logicalOperationFilter = (LogicalOperationFilter) filter;
            if (logicalOperationFilter.getFilterList().size() == 1) {
                return filter;
            }
            Item firstColumn = getSubGroupColumn(logicalOperationFilter, 0, extraCmd);
            if (firstColumn == null) {
                return filter;
            }
            // 判断可以进行合并
            OrsFilter orsFilter = ObjectCreateFactory.createOrsFilter();
            orsFilter.setColumn(firstColumn.copy());
            orsFilter.getFilterList().addAll(getSubGroupFilter(logicalOperationFilter, 0));
            for (int i = 1; i < logicalOperationFilter.getFilterList().size(); i++) {
                Item subColumn = getSubGroupColumn(logicalOperationFilter, i, extraCmd);
                if (firstColumn != null && subColumn != null && subColumn.equals(firstColumn)) {
                    orsFilter.getFilterList().addAll(getSubGroupFilter(logicalOperationFilter, i));
                } else {
                    return filter;
                }
            }
            // id = 3 or id = 4 转化为id in (3,4)
            if (isNeedExpandInToOr(extraCmd)) {// 就可以不合并了
                return orsFilter;
            }
            boolean canIn = true;
            List<Object> valueList = new ArrayList<Object>();
            for (Filter subFilter : orsFilter.getFilterList()) {
                if (subFilter.getOperation().equals(Operation.equal)) {
                    valueList.add(((BooleanFilter) subFilter).getValue());
                } else if (subFilter.getOperation().equals(Operation.in)) {
                    valueList.addAll(((BooleanFilter) subFilter).getValueList());
                } else {
                    canIn = false;
                    break;
                }
            }
            if (canIn) {
                // 优化为in
                BooleanFilter booleanFilter = ObjectCreateFactory.createBooleanFilter();
                booleanFilter.setColumn(firstColumn);
                booleanFilter.setOperation(Operation.in);
                booleanFilter.setValueList(valueList);
                return booleanFilter;
            } else {
                return orsFilter;
            }
        }

        return filter;
    }

    private static Item getSubGroupColumn(LogicalOperationFilter parent, int index, Map<String, Object> extraCmd) {
        Filter filter = parent.getFilterList().get(index);
        if (filter.getOperation().equals(Operation.or)) {
            filter = processGroupFilter(filter, extraCmd);
            parent.getFilterList().set(index, filter);
            if (filter instanceof LogicalOperationFilter) {
                // 不符合传递性,直接退出
                return null;
            }
        }
        if (filter instanceof BooleanFilter) {
            if (DnfFilters.isConstantExpressionObject(((BooleanFilter) filter).getColumn())) {
                // 列是常量
                return null;
            }
            if (!DnfFilters.isConstantExpressionObject(((BooleanFilter) filter).getValue())) {
                // value非常量
                return null;
            }
            return (Item) ((BooleanFilter) filter).getColumn();
        } else if (filter instanceof OrsFilter) {
            return (Item) ((OrsFilter) filter).getColumn();
        }

        return null;
    }

    private static List<Filter> getSubGroupFilter(LogicalOperationFilter parent, int index) {
        Filter filter = parent.getFilterList().get(index);
        List<Filter> filterList = new ArrayList<Filter>();
        if (filter instanceof OrsFilter) {
            for (Filter subFilter : ((OrsFilter) filter).getFilterList()) {
                filterList.add(subFilter);
            }
        } else {
            filterList.add(filter);
        }

        return filterList;
    }

    /**
     * 将0=1/1=1/true的恒等式进行优化
     */
    private static Filter shortFilter(Filter root) throws EmptyResultFilterException {
        Filter dnfFilter = DnfFilters.toDnfAndFlat(root);
        List<List<Filter>> filterListList = DnfFilters.toDnfFilterListList(dnfFilter);
        List<List<Filter>> newFilterListList = new ArrayList<List<Filter>>();
        for (List<Filter> filterList : filterListList) {// filterListList or是两层的外层
            boolean isShort = false;
            List<Filter> newFilterList = new ArrayList<Filter>();
            for (Filter filter : filterList) {// and是两层的内层
                if (filter.getOperation() != Operation.constant) {
                    // 不是常量
                    newFilterList.add(filter);
                } else {
                    boolean TRUE = false;
                    if (((BooleanFilter) filter).getColumn() instanceof Item) {// 可能是个not函数
                        newFilterList.add(filter);// 不能丢弃
                    } else {
                        Object value = ((BooleanFilter) filter).getColumn();
                        if (value == null) {
                            TRUE = false;
                        } else if (value.getClass() == Boolean.class || value.getClass() == boolean.class) {
                            TRUE = (Boolean) value;
                        } else {
                            // mysql中字符串'true'会被当作0处理
                            TRUE = (Type.LongType.convert(value) != 0);
                        }
                        // 真
                        if (TRUE) {
                            // x and true and y 可以直接忽略。内层的其他的filter应该不会false吧
                        } else {// 假
                            // x and false and y 内层短路了-整体false,外层就是 x or false or y,当前就可以忽略不计
                            isShort = true;
                            break;
                        }
                    }

                }
            }

            if (isShort) {
                // 存在短路 false的情况出现...x or false or y,当前就可以忽略不计
            } else {
                if (newFilterList.isEmpty()) {
                    // true and true and true，直接返回true
                    BooleanFilter booleanFilter = ObjectCreateFactory.createBooleanFilter();
                    booleanFilter.setOperation(Operation.constant);
                    booleanFilter.setColumn("1");
                    booleanFilter.setColumnName("1");
                    return booleanFilter;
                } else {// 针对非false的情况
                    newFilterListList.add(newFilterList);
                }
            }
        }

        if (newFilterListList.isEmpty()) {
            throw new EmptyResultFilterException();
        }

        return DnfFilters.orDnfFilterListList(newFilterListList);
    }

    private static Filter processBoolFilter(Filter root, boolean typeConvert, Map<String, Object> extraCmd) {
        root = exchage(root);

        if (typeConvert) {
            root = typeConvert(root);
        }

        root = expandIn(root, extraCmd);
        return root;
    }

    /**
     * 如果是1 = id的情况，转化为id = 1
     */
    private static Filter exchage(Filter root) {
        BooleanFilter booleanFilter = (BooleanFilter) root;
        if (!DnfFilters.isConstantExpressionObject(booleanFilter.getValue()) && DnfFilters.isConstantExpressionObject(booleanFilter.getColumn())) {
            Object value = booleanFilter.getColumn();
            booleanFilter.setColumn(booleanFilter.getValue());
            booleanFilter.setValue(value);
            Operation operation = booleanFilter.getOperation();
            switch (booleanFilter.getOperation()) {
                case greater_than:
                    operation = Operation.less_than;
                    break;
                case less_than:
                    operation = Operation.greater_than;
                    break;
                case greater_than_or_equal:
                    operation = Operation.less_than_or_equal;
                    break;
                case less_than_or_equal:
                    operation = Operation.greater_than_or_equal;
                    break;
                default:
                    break;
            }
            booleanFilter.setOperation(operation);
        }
        return booleanFilter;
    }

    private static Filter typeConvert(Filter root) {
        BooleanFilter booleanFilter = (BooleanFilter) root;
        // 如果是id in (xx)
        if (booleanFilter.getValueList() != null) {
            if (booleanFilter.getColumn() instanceof Column) {
                List<Object> valueList = new ArrayList<Object>();
                for (int i = 0; i < booleanFilter.getValueList().size(); i++) {
                    valueList.add(OptimizerUtils.convertType(booleanFilter.getValueList().get(i), ((Column) booleanFilter.getColumn()).getType()));
                }
                booleanFilter.setValueList(valueList);
            }
        } else {
            // 如果是 1 = id情况
            if (DnfFilters.isConstantExpressionObject(booleanFilter.getColumn()) && !DnfFilters.isConstantExpressionObject(booleanFilter.getValue())) {
                Type type = null;
                if (booleanFilter.getValue() instanceof Column) {
                    type = ((Column) booleanFilter.getValue()).getType();
                }
                booleanFilter.setColumn(OptimizerUtils.convertType(booleanFilter.getColumn(), type));
            }

            // 如果是 id = 1情况
            if (DnfFilters.isConstantExpressionObject(booleanFilter.getValue()) && !DnfFilters.isConstantExpressionObject(booleanFilter.getColumn())) {
                Type type = null;
                if (booleanFilter.getColumn() instanceof Column) {
                    type = ((Column) booleanFilter.getColumn()).getType();
                }
                booleanFilter.setValue(OptimizerUtils.convertType(booleanFilter.getValue(), type));
            }
        }
        return booleanFilter;
    }

    private static Filter expandIn(Filter root, Map<String, Object> extraCmd) {
        BooleanFilter booleanFilter = (BooleanFilter) root;
        if (booleanFilter.getOperation() == Operation.in && isNeedExpandInToOr(extraCmd)) {
            List<Object> valueList = booleanFilter.getValueList();
            Filter newRoot = null;
            for (Object value : valueList) {
                newRoot = DnfFilters.or(newRoot, Filters.equal(booleanFilter.getColumn(), value));
            }

            return newRoot;
        } else {
            return root;
        }
    }

    private static boolean isNeedExpandOrWithOther(Map<String, Object> extraCmd) {
        return ExtraCmd.getExtraCmdBoolean(extraCmd, ConnectionProperties.expand_or_with_other, false);
    }

    private static boolean isNeedExpandInToOr(Map<String, Object> extraCmd) {
        return ExtraCmd.getExtraCmdBoolean(extraCmd, ConnectionProperties.expand_in_to_or, false);
    }
}
